//Event.cpp
//This cpp file deals with creation and adjustment of Event.
//@author A0101038Y
//@class Event
#include "Event.h"

string Event::sendStatusEvent(STATUS_TYPE_EVENT status) const {
	//send status event to enable done/not done
	switch (status){
		case DONE:
			return STATUS_DONE;
			break;
		case NOT_DONE:
			return STATUS_NOT_DONE;
			break;
	}
	return STATUS_UNSPECIFIED; // default case
}
Event::Event(void): _statusCompleted(false)
{
	_entryType=TYPE_ENTRY;
}
Event::Event(string name, QDate endDate, QTime endTime, QDate startDate, QTime startTime, bool statusCompleted)
	: Entry(name, endDate, endTime), _startDate(startDate), _startTime(startTime), _statusCompleted(statusCompleted) {
	_entryType=TYPE_ENTRY;
	setDefaultStartEndDateTime(); //set default for end date and end time if there is missing details, and to resolve start date > end date
}
Event::Event(const Entry* other): Entry(other->getName(), other->getEndDate(), other->getEndTime()), _startDate(other->getStartDate()), _startTime(other->getStartTime()), _statusCompleted(other->getStatus()) {
	_entryType=TYPE_ENTRY;
	setDefaultStartEndDateTime(); //set default for end date and end time if there is missing details, and to resolve start date > end date
}
Event::~Event(void)
{
}
bool Event::isEmpty() {
	return (Entry::isEmpty());
}
QDate Event::getStartDate() const {
	return _startDate;
}
QTime Event::getStartTime() const {
	return _startTime;
}
bool Event::isConflict(Entry* newEvent) { //enable identification of conflicting events
	if(getEndDate().isValid() && getEndTime().isValid() && getStartDate().isValid() && getStartTime().isValid() &&
		newEvent->getEndDate().isValid() && newEvent->getEndTime().isValid() && newEvent->getStartDate().isValid() && newEvent->getStartTime().isValid()){
		int endToNewEnd = QDateTime(getEndDate(), getEndTime()).secsTo(QDateTime(newEvent->getEndDate(), newEvent->getEndTime()));
		int newStartToNewEnd =QDateTime(newEvent->getStartDate(),newEvent->getStartTime()).secsTo(QDateTime(newEvent->getEndDate(), newEvent->getEndTime()));
		if(endToNewEnd>0) {
			return endToNewEndSmaller(endToNewEnd, newStartToNewEnd);
		}
		int newEndToEnd = QDateTime(newEvent->getEndDate(), newEvent->getEndTime()).secsTo(QDateTime(getEndDate(), getEndTime()));
		int startToEnd =QDateTime(getStartDate(),getStartTime()).secsTo(QDateTime(getEndDate(), getEndTime()));
		if(endToNewEnd<0) {
			return newEndToEndSmaller(newEndToEnd, startToEnd);
		}
		if(endToNewEnd ==0) {
			return true;
		}
		return false;
	} else {
		return false;
	}
}
string Event::toString() { //loading of vector of strings to string for storing
	char buffer[1000];
	sprintf_s(buffer,MSG_OUTPUT.c_str(), 
		TYPE_ENTRY.c_str(), getName().c_str(), 
		getEndDate().toString(FORMAT_DATE).toStdString().c_str(),getEndTime().toString(FORMAT_TIME).toStdString().c_str(), 
		getStartDate().toString(FORMAT_DATE).toStdString().c_str(),getStartTime().toString(FORMAT_TIME).toStdString().c_str(), _statusCompleted);
	return buffer;
}
Event Event::fromString(string input) { //loading of string from storage file to vector of string
	istringstream inStream(input);
	string name;
	string endDate;
	string endTime;
	string startDate;
	string startTime;
	string dump;
	bool statusCompleted;
	getline(inStream, dump, ' ');
	getline(inStream, name);
	getline(inStream, dump, ' ');
	getline(inStream, endDate);
	getline(inStream, dump, ' ');
	getline(inStream, endTime);
	getline(inStream, dump, ' ');
	getline(inStream, startDate);
	getline(inStream, dump, ' ');
	getline(inStream, startTime);
	getline(inStream, dump, ' ');
	inStream >> statusCompleted;
	return Event(name, QDate::fromString(QString::fromStdString(endDate), FORMAT_DATE), QTime::fromString(QString::fromStdString(endTime), FORMAT_TIME),
		QDate::fromString(QString::fromStdString(startDate),FORMAT_DATE), QTime::fromString(QString::fromStdString(startTime),FORMAT_TIME), statusCompleted);
}
bool Event::operator==(Entry* input) {
	return (getName()==input->getName() &&
		getEndDate()==input->getEndDate() &&
		getEndTime()==input->getEndTime() &&
		getStartDate()==input->getStartDate() &&
		getStartTime()==input->getStartTime() &&
		getEntryType()==input->getEntryType() &&
		getStatus()==input->getStatus());
}
void Event::setStartDate(QDate startDate, CommonEnum::CALLER caller) { //setting start date for new entry and for edited entry
	if(isFloat()) {
		setFloatStartDate(startDate, caller);
	}
	QDate oldDate = _startDate;
	_startDate=startDate;
	QTime oldTime = _startTime;
	if(caller != CommonEnum::CALLER_TOKENISER){
		if(getStartTime().isValid()){
			correctStartDateTimeValid(caller, oldDate, oldTime);
		} else{
			_startTime = QTime::QTime(00,00); //set default if time is NULL
			oldTime = _startTime;
			correctStartDateTimeValid(caller, oldDate, oldTime);
		}
	}
}
void Event::setStartTime(QTime startTime, CommonEnum::CALLER caller) { //setting start time for new entry and for edited entry
	if(isFloat()) {
		setFloatStartTime(startTime, caller);
	}
	QTime oldTime = _startTime;
	_startTime=startTime;
	QDate oldDate = _startDate;
	if(caller != CommonEnum::CALLER_TOKENISER){
		if(getStartDate().isValid()){
			correctStartDateTimeValid(caller, oldDate, oldTime);
		} else{
			_startDate = QDate::currentDate(); //set default if date is NULL
			oldDate = _startDate;
			correctStartDateTimeValid(caller, oldDate, oldTime);
		}
	}
}
string Event::getEntryType() const {
	return _entryType;
}
//void Event::setName(string name) {
//	Entry::setName(name);
//}
string Event::getSearchString() const { //searching of string
	char buffer[1000];
	sprintf_s(buffer ,SEARCH_PLACEHOLDER, getName().c_str(), getEndDate().toString(SEARCH_DATE_FORMAT).toStdString().c_str(), getEndTime().toString(SEARCH_TIME_FORMAT).toStdString().c_str(), getStartDate().toString(SEARCH_DATE_FORMAT).toStdString().c_str(), getStartTime().toString(SEARCH_TIME_FORMAT).toStdString().c_str(), getStatusString().c_str());
	return buffer;
}
int Event::getAttributeCount() const {
	return Event::NUM_ATTRIBUTES;
}
bool Event::setDone() { //setting done to event
	if(_statusCompleted) {
		_statusCompleted = false;
	} else {
		_statusCompleted = true;
	}
	return _statusCompleted;
}
bool Event::getStatus() const { //return status of event
	return _statusCompleted;
}
string Event::getStatusString() const { //return string of status for event
	if(_statusCompleted){
		return sendStatusEvent(DONE);
	} else{
		return sendStatusEvent(NOT_DONE);
	}
}
bool Event::isOngoingOrNot(QDateTime target) const {
	//showing ongoing events only if startdatetime and enddatetime is valid.
	QDate const startDate = getStartDate();
	QTime const startTime = getStartTime();
	QDate const endDate = getEndDate();
	QTime const endTime = getEndTime();
	if(!(startDate.isValid() && startTime.isValid() && endDate.isValid() && endTime.isValid())){
		return false;
	} else {
		QDateTime const eventStartDateTime (startDate, startTime); 
		QDateTime const eventEndDateTime(endDate, endTime); 
		return ((eventEndDateTime > target)&&(eventStartDateTime < target));
	}
}
void Event::setDefaultStartEndDateTime(){
	//setting default start and end date or time if there is alteration in .txt storage file by user. It is a guard against unauthorize edit to prevent program crash.
	if(isFloat()){
		// exclude floating events from being corrected by default case should user edits the storage text file.
		return;
	} else {
		if(getStartDate().isValid() && getStartTime().isValid() && getEndDate().isValid() && getEndTime().isValid()){
			//handle case where attributes are altered such that start date or time is later than end date or time
			QDate startDate = getStartDate();
			QTime startTime = getStartTime();
			QDate endDate = getEndDate();
			QTime endTime = getEndTime();
			QDateTime startDateTime (startDate, startTime);
			QDateTime endDateTime (endDate, endTime);
			if(!isStartEndDateTimeValid(startDateTime, endDateTime)){
				setDefaultEndEvent(startDate, startTime);
				return;
			} else {
				return; 
			}
		} else {
			if(!getEndDate().isValid() || !getEndTime().isValid()){
				//handle case where storage text is altered such that end date or time is missing. Set them to start
				completeMissingEndDateTimeAttributes();
				return;
			} else {
				if(!getStartDate().isValid() || !getStartTime().isValid()){
					//handle case where storage text is altered such that start date or time is missing. Set them to default start or end date time
					completeMissingStartDateTimeAttributes();
					QDate startDate = _startDate;
					QTime startTime = _startTime;
					QDate endDate = getEndDate();
					QTime endTime = getEndTime();
					QDateTime startDateTime (startDate, startTime);
					QDateTime endDateTime (endDate, endTime);
					if(!isStartEndDateTimeValid(startDateTime, endDateTime)){
						changeDefaultToEndDateTimeEvent(endDate, endTime);
						return;
					} else {
						return;
					}
				} else {
					return;
				}
			}
		}
	}
}
void Event::correctStartDateTimeValid(CommonEnum::CALLER caller, QDate oldDate, QTime oldTime){
	//function correct invalid edit where end date or time is earlier than start date or time (for start date or time edit)
	if(getStartDate().isValid() && getStartTime().isValid() && getEndDate().isValid() && getEndTime().isValid()){ 	//do nothing when any date or time is not valid.
		QDate startDate = getStartDate();
		QTime startTime = getStartTime();
		QDate endDate = getEndDate();
		QTime endTime = getEndTime();
		QDateTime startDateTime (startDate, startTime);
		QDateTime endDateTime (endDate, endTime);
		if(caller == CommonEnum::CALLER_ENTRIESMANAGER){//validate only items from Entriesmanager. 
			if(!isStartEndDateTimeValid(startDateTime, endDateTime)){ 
				revertInvalidEditStartDateTime(oldDate, oldTime); //revert changes made in setEndDate and setEndTime.
				throw BotException(BotException::EXCEPTION_START_END_CONFLICT); //throw exception when end is earlier than start.
			} else {
				return;
			}
		} else {
			return;
		}
	}
}
void Event::revertInvalidEditStartDateTime(QDate oldDate, QTime oldTime){
	//revert changes made previously in setDate or setTime function (for start date or time edit)
	_startDate = oldDate;
	_startTime = oldTime;
}
void Event::setFloatStartDate(QDate startDate, CommonEnum::CALLER caller){ //set Start Date for floating Event
	if(caller != CommonEnum::CALLER_TOKENISER){
		_startDate = startDate;
		_startTime = QTime::QTime(00,00);
		_endDate = _startDate;
		_endTime = _startTime;
		}
}
void Event::setFloatStartTime(QTime startTime, CommonEnum::CALLER caller){ //set Start Time for floating Event.
	if(caller != CommonEnum::CALLER_TOKENISER){
		_startDate = QDate::currentDate();
		_startTime = startTime;
		_endDate = _startDate;
		_endTime = _endTime;
	}
}
void Event::setFloatEndDate(QDate endDate, CommonEnum::CALLER caller){ //set End Date for Floating Event.
	if(caller != CommonEnum::CALLER_TOKENISER){
		_endTime = QTime::QTime(00,00);
		_endDate = endDate;
		_startDate = _endDate;
		_startTime =_endTime;
	}
}
void Event::setFloatEndTime(QTime endTime, CommonEnum::CALLER caller){ //set End Time for floating Event.
	if(caller != CommonEnum::CALLER_TOKENISER){
		_endDate = _startDate;
		_startDate = QDate::currentDate();
		_endTime = endTime;
		_startTime =_endTime;
	}
}
void Event::setDefaultEndEvent(QDate startDate, QTime startTime){ //correct end Date and Time so that it is later of at the same time as Start Date and Time when End attributes are missing
	_endDate = startDate;
	_endTime = startTime;
	return;
}
void Event::changeDefaultToEndDateTimeEvent(QDate endDate, QTime endTime){ //correct start date and time so it is earlier or at the same time as end when start attributes are missing.
	_startDate = endDate;
	_startTime = endTime;
	return;
}
void Event::completeMissingEndDateTimeAttributes(){ //complete missing attributes in .txt storage file
	_endDate = getStartDate();
	_endTime = getStartTime();
	return;
}
void Event::completeMissingStartDateTimeAttributes(){ //complete missing attributes in .txt storage file
	_startDate = QDate::currentDate();
	_startTime = QTime::QTime(00, 00);
	return;
}
bool Event::endToNewEndSmaller(int endToNewEnd, int newStartToNewEnd){ //comparison for inConflict function
	if(endToNewEnd<newStartToNewEnd) {
		return true;
	} else {
		return false;
	}
}
bool Event::newEndToEndSmaller(int newEndToEnd, int startToEnd){	//comparison for inConflict function
	if(newEndToEnd < startToEnd) {
		return true;
	} else {
		return false;
	}
}
const string Event::MSG_OUTPUT= "%s\nNAME: %s\nDUE_DATE: %s\nDUE_TIME: %s\nSTART_DATE: %s\nSTART_TIME: %s\nSTATUS: %d\n";
const string Event::TYPE_ENTRY= "EVENT";
const string Event::STATUS_DONE = "Done";
const string Event::STATUS_NOT_DONE = "Not Done";
const string Event::STATUS_UNSPECIFIED = "Unspecified";
const char* Event::SEARCH_PLACEHOLDER= "%s %s %s %s %s %s";
const char* Event::SEARCH_DATE_FORMAT="dddd MMMM yyyy dd/MM/yy dd/MM/yyyy dd/M/yy dd/M/yyyy";
const char* Event::SEARCH_TIME_FORMAT="h:mm hmmap H:mm Hmm";